/**
 * LinkedLST
 * May 13, 2011
 * BarcodeView.java - Review activity for viewing list of scanned
 * 	items and marking them as picked up.
 */

package edu.washington.cs.labspecimentransport;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import edu.washington.cs.labspecimentransport.data.DataQuery;
import edu.washington.cs.labspecimentransport.ops.CommonHandlers;
import edu.washington.cs.labspecimentransport.ops.Conversion;
import edu.washington.cs.labspecimentransport.ops.Keys;

/**
 * A ReviewView displays a list of the barcodes of items that has been scanned during 
 * a scan session. Options are provided for additional scanning or marking the scanned
 * items as picked up.
 * 
 * <p>
 * An intent with the following keys are expected:
 * 	Map<Integer,ArrayList<String>> : KEY_SPECIMENMAP - mapping of ids to barcodes for specimens that need to be picked up
 * 	Map<Integer,ArrayList<String>> : KEY_CONTAINERMAP - mapping of ids to barcodes for containers that need to be picked up		
 * 	List<String> : KEY_SCANNEDITEMS - list of scanned item barcodes
 * 	int : KEY_COURIERID - the courier id
 * 	int : KEY_LOCATIONID - the current location id
 */
public class ReviewView extends Activity {
	
	private ItemAdapter adapter;
	private Button pickup;
	
	// mapping of item ids to item barcodes for specimens and containers
	private HashMap<Integer, ArrayList<String>> specimensAndBarcodes;	
	private HashMap<Integer, ArrayList<String>> containersAndBarcodes;
	
	private ArrayList<String> scannedBarcodes;	// list of scanned item barcodes
	private int courierId;						// the courier id
	private int locationId;						// the current location id
	
	private int selectedPosition;				// the last selected ListView index
	private String selectedBarcode;				// the barcode at that index
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	// standard initialization and lock screen orientation to portrait
        super.onCreate(savedInstanceState);
        setContentView(R.layout.review);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        getPassedData();	// get required information from intent
        setupButtons();		// setup button onClick handlers

        updateCountDisplay();
        
        // set the adapter for the ListView display of scanned items
        adapter = new ItemAdapter(this, scannedBarcodes);
        ListView listScanned = (ListView)findViewById(R.id.review_list);
        listScanned.setAdapter(adapter);
        
        registerForContextMenu(listScanned);
    }

    /**
     * Finish this activity when back button is pressed
     */
    public void onBackPressed() {
    	returnToScanning();
    }
    
    /**
     * Define the options menu.
     */
    public boolean onCreateOptionsMenu(Menu menu) {
    	menu.add(0, Keys.MENUID_HOME, 0, R.string.option_home);
    	menu.add(0, Keys.MENUID_HELP, 0, R.string.option_help);
    	return true;
    }

    /**
     * Define functionality for each item in options menu.
     */
    public boolean onOptionsItemSelected(MenuItem item) {   
        switch(item.getItemId()) {
        
        case Keys.MENUID_HOME:
        	CommonHandlers.returnHome(this);
        	break;
        	
        case Keys.MENUID_HELP:
        	CommonHandlers.startHelp(this);
        	break;
        	
        default:
        	super.onOptionsItemSelected(item);
        	break;
        }
        
        return true;
    }
    
    /**
     * Define the context menu for list of scanned barcodes.
     */
    public void onCreateContextMenu(ContextMenu menu, View v,
    		ContextMenuInfo menuInfo) {
    	super.onCreateContextMenu(menu, v, menuInfo);
    	menu.setHeaderTitle(getString(R.string.context_header));
    	
    	AdapterView.AdapterContextMenuInfo info =
            (AdapterView.AdapterContextMenuInfo) menuInfo;
    	LinearLayout listItem = (LinearLayout) info.targetView;
		TextView selectedView = (TextView) listItem.findViewById(R.id.review_item);
		
    	selectedPosition = info.position;
      	selectedBarcode = selectedView.getText().toString();
    	
    	MenuInflater inflater = getMenuInflater();
    	inflater.inflate(R.layout.review_single_menucontext, menu);
    }
    
    /**
     * Define functionality for each item in context menu.
     */
    public boolean onContextItemSelected(MenuItem item) {
    	super.onContextItemSelected(item);
    	  switch (item.getItemId()) {
    	  
    	  // open ItemView
    	  case R.id.review_more_info:
    		  Intent i = new Intent(ReviewView.this, ItemView.class);
    		  Bundle b = new Bundle();
    		  b.putAll(getIntent().getExtras());
    		  b.putSerializable(Keys.KEY_SCANNEDITEMS, new ArrayList<String>());
    		  
    		  // get the item id and whether or not the item is a container
    		  int itemId = 0;
    		  if (Conversion.mapContainsBarcode(specimensAndBarcodes, selectedBarcode)) {
    			  for (Integer id : specimensAndBarcodes.keySet()) {
    				  if (specimensAndBarcodes.get(id).contains(selectedBarcode)) {
    					  itemId = id;
    					  break;
    				  }
    			  }
    			  b.putBoolean(Keys.KEY_ISCONTAINER, false);
    			  b.putStringArrayList(Keys.KEY_BARCODE, specimensAndBarcodes.get(itemId));
    		  } else {
    			  for (Integer id : containersAndBarcodes.keySet()) {
    				  if (containersAndBarcodes.get(id).contains(selectedBarcode)) {
    					  itemId = id;
    					  break;
    				  }
    			  }
    			  b.putBoolean(Keys.KEY_ISCONTAINER, true);
    			  b.putStringArrayList(Keys.KEY_BARCODE, containersAndBarcodes.get(itemId));
    		  }
			  b.putInt(Keys.KEY_ITEMID, itemId);
    		  
			  i.putExtras(b);
			  startActivity(i);
			  
    		  return true;
    	   
    	  // remove item from list of scanned items
    	  case R.id.review_remove:
    		  scannedBarcodes.remove(selectedPosition);
    		  adapter.notifyDataSetChanged();
    		  updateCountDisplay();
    		  return true;
    		  
    	  default:
    		  return super.onContextItemSelected(item);
    	  }
    }
    
    /**
     * Updates the display for count of scanned items.
     */
    private void updateCountDisplay() {
    	String display;
    	if (scannedBarcodes == null || scannedBarcodes.size() == 0) {
    		display = "No items scanned";
    		pickup.setText("Back");
    	} else if (scannedBarcodes.size() == 1) {
    		display = "1 item scanned";
    	} else {
    		display = "" + scannedBarcodes.size() + " items scanned";
    	}
    	
        // set the screen header (number of items scanned)
        TextView headerTitle = (TextView) findViewById(R.id.reviewHeader);
        headerTitle.setText(display);
    }

    /**
     * Extract data from intent
     */
    @SuppressWarnings("unchecked")
	private void getPassedData() {
        Bundle bundle = getIntent().getExtras();
        specimensAndBarcodes = (HashMap<Integer, ArrayList<String>>) bundle.getSerializable(Keys.KEY_SPECIMENMAP);
        containersAndBarcodes = (HashMap<Integer, ArrayList<String>>) bundle.getSerializable(Keys.KEY_CONTAINERMAP);
        scannedBarcodes = bundle.getStringArrayList(Keys.KEY_SCANNEDITEMS);
        courierId = bundle.getInt(Keys.KEY_COURIERID);
        locationId = bundle.getInt(Keys.KEY_LOCATIONID);
    }

    /**
     * Define the onClick handlers for all buttons
     */
    private void setupButtons() {
    	// set onClick event handler for the clear button
    	Button clear = (Button) findViewById(R.id.home_button);
    	clear.setOnClickListener(new View.OnClickListener() {
    		public void onClick(View view) {
    			// display an alert dialog asking for confirmation
    			AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
    			builder.setMessage(getString(R.string.review_dialog_clearList));
    			builder.setCancelable(false);

    			builder.setPositiveButton(getString(R.string.dialog_yes), 
    					new DialogInterface.OnClickListener() {
    				public void onClick(DialogInterface dialog, int id) {
    					scannedBarcodes.clear();
    					adapter.notifyDataSetChanged();
    					updateCountDisplay();
    				}
    			});

    			builder.setNegativeButton(getString(R.string.dialog_cancel), 
    					new DialogInterface.OnClickListener() {
    				public void onClick(DialogInterface dialog, int id) {
    					dialog.cancel();
    				}
    			});
    			AlertDialog alert = builder.create();
    			alert.show();
    		}
    	});
        
        // set onClick event handler for "Scan more" button
        Button scanMore = (Button) findViewById(R.id.review_scan_more);
        scanMore.setOnClickListener(new View.OnClickListener() {
        	
        	// keep the state of the current scan session and return to the scanning view
            public void onClick(View view) {
                returnToScanning();
            }
        });
        
        // onClick event handler for "Pickup" button
        pickup = (Button) findViewById(R.id.review_pickup);
        pickup.setOnClickListener(new View.OnClickListener() {
        	
        	// mark the items as picked up in the database and return to client view
            public void onClick(View view) {
            	new pickupItems().execute();
            }
        });
    }
    
    /**
     * Finishes this activity and return any cleared scan entries.
     */
    private void returnToScanning() {
    	Intent intent = new Intent(ReviewView.this, BarcodeView.class);
        Bundle newBundle = new Bundle();
        newBundle.putAll(getIntent().getExtras());
        newBundle.putStringArrayList(Keys.KEY_SCANNEDITEMS, scannedBarcodes);
        intent.putExtras(newBundle);
        setResult(Activity.RESULT_OK, intent);
        finish();
    }
    
    /**
     * Get a list of item ids from a list of scanned barcodes.
     * @return list of item ids
     */
    private List<Integer> getIdListFromScannedBarcodes() {
    	List<Integer> ids = new ArrayList<Integer>();
    	
    	for (Integer id : specimensAndBarcodes.keySet()) {
    		for (String currBarcode : specimensAndBarcodes.get(id)) {
    			if (scannedBarcodes.contains(currBarcode)) {
    				ids.add(id);
    			}
    		}
    	}
    	
    	for (Integer id : containersAndBarcodes.keySet()) {
    		for (String currBarcode : containersAndBarcodes.get(id)) {
    			if (scannedBarcodes.contains(currBarcode)) {
    				ids.add(id);
    			}
    		}
    	}
    	return ids;
    }
    
    /**
     * Adapter for displaying list of scanned barcodes
     */
    private class ItemAdapter extends BaseAdapter {

    	private Context context;
    	private List<String> items;
    	
		public ItemAdapter(Context context, List<String> items) {
			this.context = context;
			this.items = items;
		}

		public int getCount() {
			return items.size();
		}

		public String getItem(int pos) {
			return items.get(pos);
		}

		public long getItemId(int pos) {
			return items.indexOf(this.getItem(pos));
		}

		public View getView(int position, View view, ViewGroup parent) {
			String item = this.getItem(position);
			LinearLayout itemLayout = (LinearLayout) 
				LayoutInflater.from(context).inflate(R.layout.review_single, parent, false);
			
			TextView itemIdField = (TextView) itemLayout.findViewById(R.id.review_item);
			itemIdField.setText(item);

			return itemLayout;
		}
    }
    
	/**
	 * Service to mark items as picked up. Not cancelable.
	 */
	private class pickupItems extends AsyncTask<Void, Void, Void> {
		private ProgressDialog dialog;
		private boolean success;
		private DataQuery dq;
		
		// before execution
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			dq = DataQuery.getInstance();
			success = true;
			dialog = ProgressDialog.show(ReviewView.this, "", 
					getString(R.string.review_load_pickup), true);
		}
		
		// execution
		@Override
		protected Void doInBackground(Void... params) {
			try {
            	List<Integer> ids = getIdListFromScannedBarcodes();
            	dq = DataQuery.getInstance();
            	
            	// send update to server only if there are scanned barcodes
            	if (!ids.isEmpty()) {
            		Log.d("DERP", "Marking the following items as picked up");
            		Log.d("DERP", ids.toString());
            		dq.markItemsAsPickedUp(courierId, locationId, ids);
            	}
            	
			} catch (NullPointerException e) {
				success = false;
			}
			
			return null;
		}
		
		// after execution
		@Override
		protected void onPostExecute(Void unused) {
			dialog.dismiss();
			
			if (success) {
				// return to ClientView on success
				Intent intent = new Intent(ReviewView.this, ClientView.class);
				Bundle newBundle = new Bundle();
				newBundle.putAll(getIntent().getExtras());
				newBundle.putStringArrayList(Keys.KEY_SCANNEDITEMS, new ArrayList<String>());
				intent.putExtras(newBundle);

				dq.flushEntireCache();

				startActivity(intent);
			} else {
				Toast toast = Toast.makeText(ReviewView.this, 
						R.string.error_send, Toast.LENGTH_LONG);
				toast.show();
			}
		}
	}
}
